/*
 * 描          述:  <描述>
 * 修  改   人:  Administrator
 * 修改时间:  2017年10月16日
 * <修改描述:>
 */
package com.tx.component.dailytask.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.transaction.TransactionManager;

import com.tx.component.dailytask.executor.DailyTaskExecutor;
import com.tx.component.dailytask.executor.impl.AsyncBatchDailyTaskExecutor;
import com.tx.component.dailytask.executor.impl.BatchDailyTaskExecutor;
import com.tx.component.dailytask.executor.impl.IterableDailyTaskExecutor;
import com.tx.component.dailytask.executor.impl.SimpleDailyTaskExecutor;
import com.tx.component.dailytask.task.IDailyTask;
import com.tx.component.dailytask.task.impl.AsyncBatchDailyTask;
import com.tx.component.dailytask.task.impl.BatchDailyTask;
import com.tx.component.dailytask.task.impl.IterableDailyTask;
import com.tx.component.dailytask.task.impl.SimpleDailyTask;
import com.tx.component.task.exeception.TaskInitException;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.util.MessageUtils;

/**
  * 定时任务执行器工厂<br/>
  * <功能详细描述>
  * 
  * @author  Administrator
  * @version  [版本号, 2017年10月16日]
  * @see  [相关类/方法]
  * @since  [产品/模块版本]
  */
public class DailyTaskExecutorRegistry implements ApplicationContextAware,
        InitializingBean, BeanFactoryAware, BeanPostProcessor {
    
    /** 定时任务执行器工厂 */
    private Logger logger = LoggerFactory
            .getLogger(DailyTaskExecutorRegistry.class);
    
    /** spring容器 */
    private ApplicationContext applicationContext;
    
    /** Bean定义注册机 */
    protected BeanDefinitionRegistry beanDefinitionRegistry;
    
    /** 单例对象注册方法 */
    protected SingletonBeanRegistry singletonBeanRegistry;
    
    /** 事务transactionManager */
    protected TransactionManager transactionManager;
    
    /**
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
    }
    
    /**
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        AssertUtils.isInstanceOf(BeanDefinitionRegistry.class,
                beanFactory,
                "beanFactory is not BeanDefinitionRegistry instance.");
        this.beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory;
        
        AssertUtils.isInstanceOf(SingletonBeanRegistry.class,
                beanFactory,
                "beanFactory is not SingletonBeanRegistry instance.");
        this.singletonBeanRegistry = (SingletonBeanRegistry) beanFactory;
    }
    
    /**
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
    }
    
    /**
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean instanceof IDailyTask) {
            registeTaskExecutor((IDailyTask) bean);
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean,
                beanName);
    }
    
    /**
     * 根据任务Id获取对应的Executor实例<br/>
     * <功能详细描述>
     * @param taskId
     * @return [参数说明]
     * 
     * @return DailyTaskExecutor [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public DailyTaskExecutor getDailyTaskExecutor(String taskId) {
        AssertUtils.notEmpty(taskId, "taskId is empty.");
        
        String executorBeanName = getTaskExecutorName(taskId);
        Object obj;
        try {
            obj = this.applicationContext.getBean(executorBeanName);
        } catch (NoSuchBeanDefinitionException e) {
            //donothing
            return null;
        }
        if (obj instanceof DailyTaskExecutor) {
            return (DailyTaskExecutor) obj;
        }
        return null;
    }
    
    /**
     * @desc 向spring容器注册BeanDefinition
     * @param beanName
     * @param beanDefinition
     */
    protected Object registerBeanDefinition(String beanName,
            BeanDefinition beanDefinition) {
        if (!this.beanDefinitionRegistry.containsBeanDefinition(beanName)) {
            this.beanDefinitionRegistry.registerBeanDefinition(beanName,
                    beanDefinition);
        }
        Object bean = this.applicationContext.getBean(beanName);
        return bean;
    }
    
    /**
     * 构建默认的基础数据业务类<br/>
     * <功能详细描述>
     * @param type [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    //@SuppressWarnings("rawtypes")
    protected void registeTaskExecutor(IDailyTask task) {
        String executorBeanName = getTaskExecutorName(task.getId());
        Class<? extends DailyTaskExecutor> executorClass = buildTaskExecutorClass(
                task);
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(executorClass);
        builder.addPropertyValue("task", task);
        builder.addPropertyValue("beanName", executorBeanName);
        builder.addPropertyValue("transactionManager", this.transactionManager);
        
        logger.info("完成任务执行器注册：task:{}", new Object[] { task.getClass() });
        registerBeanDefinition(executorBeanName, builder.getBeanDefinition());
    }
    
    /** 
     * 生成对应的业务层Bean名称<br/>
     * <功能详细描述>
     * @param type
     * @return [参数说明]
     * 
     * @return String [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    protected String getTaskExecutorName(String taskId) {
        String taskExecutorBeanName = "TaskExecutor[" + taskId + "]";
        return taskExecutorBeanName;
    }
    
    /**
     * 构建定时任务<br/>
     * <功能详细描述>
     * @param task
     * @return [参数说明]
     * 
     * @return TimedTaskExecutor [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    protected Class<? extends DailyTaskExecutor> buildTaskExecutorClass(
            IDailyTask task) {
        AssertUtils.notNull(task, "task is null.");
        
        Class<? extends DailyTaskExecutor> executorType = null;
        if (task instanceof SimpleDailyTask) {
            executorType = SimpleDailyTaskExecutor.class;
        } else if (task instanceof BatchDailyTask) {
            executorType = BatchDailyTaskExecutor.class;
        } else if (task instanceof IterableDailyTask) {
            executorType = IterableDailyTaskExecutor.class;
        } else if (task instanceof AsyncBatchDailyTask) {
            executorType = AsyncBatchDailyTaskExecutor.class;
        } else {
            throw new TaskInitException(MessageUtils.format(
                    "task init exception.TaskExecutor not exist,task type:{}",
                    new Object[] { task.getClass().getName() }));
        }
        
        return executorType;
    }
    
    /**
     * @param 对transactionManager进行赋值
     */
    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
    
}
